﻿Shader "Unity Shades Book/Chapter 13/Edge Detect Normal And Depth"
{
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _EdgeOnly ("Edge Only", Float) = 1.0
        _EdgeColor ("Edge Color", Color) = (0.0, 0.0, 0.0, 1.0)
        _BackgroundColor ("Background Color", Color) = (1.0, 1.0, 1.0, 1.0)
        _SampleDistance ("Sample Distance", Float) = 1.0
        _Sensitivity ("Sensitivity", Vector) = (1.0, 1.0, 1.0, 1.0)
    }

    SubShader
    {
        CGINCLUDE

        #include "UnityCG.cginc"

        // 主纹理
        sampler2D _MainTex;
        // 主纹理的纹素
        half4 _MainTex_TexelSize;
        // 只显示边缘
        fixed _EdgeOnly;
        // 边缘颜色
        fixed4 _EdgeColor;
        // 背景颜色
        fixed4 _BackgroundColor;
        // 采样距离
        float _SampleDistance;
        // 控制判定为边界的密度（只用到x、y分量）
        half4 _Sensitivity;
        sampler2D _CameraDepthNormalsTexture;

        struct v2f
        {
            float4 pos : SV_POSITION;
            // 存储当前片元的UV值以及邻域四个片元的UV值
            // （右上、左下、左上、右下）
            half2 uv[5] : TEXCOORD0;
        };

        v2f vert(appdata_img v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);

            half2 uv = v.texcoord;
            o.uv[0] = uv;

            #if UNITY_UV_STARTS_AT_TOP
            if (_MainTex_TexelSize.y < 0)
                uv.y = 1 - uv.y;
            #endif

            // 存储邻域UV值，用于片元着色器使用Roberts算子时的采样坐标
            o.uv[1] = uv + _MainTex_TexelSize.xy * half2(1, 1) * _SampleDistance;
            o.uv[2] = uv + _MainTex_TexelSize.xy * half2(-1, -1) * _SampleDistance;
            o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1, 1) * _SampleDistance;
            o.uv[4] = uv + _MainTex_TexelSize.xy * half2(1, -1) * _SampleDistance;

            return o;
        }

        half CheckSame(half4 center, half4 sample)
        {
            // 注意：
            // 这里因为只需要比较法线值，所以不需要精确地求得真正的法线值
            // （没有像上一节一样将采样的法线值转为视角空间下的线性法线值）

            // 得到法线1
            half2 centerNormal = center.xy;
            // 得到深度值1（本质是z w分量相加得到一个数值）
            /**
            inline float DecodeFloatRG( float2 enc )
            {
                float2 kDecodeDot = float2(1.0, 1/255.0);
                return dot( enc, kDecodeDot );
            }
            */
            // 等同于 x / 255 + y
            float centerDepth = DecodeFloatRG(center.zw);

            // 得到法线2
            half2 sampleNormal = sample.xy;
            // 得到深度值2
            float sampleDepth = DecodeFloatRG(sample.zw);

            // 计算法线的偏移程度，用_Sensitivity.x来控制判定程度
            half2 diffNormal = abs(centerNormal - sampleNormal) * _Sensitivity.x;
            // 取法线偏差的x、y相加（经验模型）来判定两条法线是否接近相同的方向
            int isSameNormal = (diffNormal.x + diffNormal.y) < 0.1;

            // 计算深度的相差程度，用_Sensitivity.y来控制判定程度
            float diffDepth = abs(centerDepth - sampleDepth) * _Sensitivity.y;
            // 用偏差是否达到其中一个深度的十分之一来判定深度值是否相近
            int isSameDepth = diffDepth < 0.1 * centerDepth;

            // 只有深度和法线都差不多接近，才返回1；
            // 否则返回0
            return isSameNormal * isSameDepth ? 1.0 : 0.0;
        }

        fixed4 fragRobertsCrossDepthAndNormal(v2f i) : SV_Target
        {
            // 对深度法线纹理，当前像素的邻域进行采样
            half4 sample1 = tex2D(_CameraDepthNormalsTexture, i.uv[1]);
            half4 sample2 = tex2D(_CameraDepthNormalsTexture, i.uv[2]);
            half4 sample3 = tex2D(_CameraDepthNormalsTexture, i.uv[3]);
            half4 sample4 = tex2D(_CameraDepthNormalsTexture, i.uv[4]);

            // 代入Roberts算子的公式：
            // 左上、右下差值 * 左下、右上差值
            // edge的最终值只会是0或1，只要任意一个不是相近，则判定它是边界
            half edge = 1.0;
            edge *= CheckSame(sample1, sample2);
            edge *= CheckSame(sample3, sample4);

            // 边缘颜色、当前像素颜色的插值
            fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv[0]), edge);
            // 边缘颜色、背景色的插值
            fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);

            // 根据_EdgeOnly参数，来控制是否结合像素颜色的程度
            return lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly);
        }

        ENDCG

        Pass
        {
            ZTest Always
            Cull Off
            ZWrite Off

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment fragRobertsCrossDepthAndNormal

            ENDCG
        }
    }

    Fallback Off
}
